package com.vista.questionnaire.service;

import com.vista.questionnaire.domain.Answer;
import com.vista.questionnaire.domain.AnswerDetail;
import com.vista.questionnaire.domain.Examination;
import com.vista.questionnaire.domain.Question;
import com.vista.questionnaire.exception.BizException;
import com.vista.questionnaire.exception.ErrorDescriptor;
import com.vista.questionnaire.repository.AnswerDetailRepository;
import com.vista.questionnaire.repository.AnswerRepository;
import com.vista.questionnaire.repository.ExaminationRepository;
import com.vista.questionnaire.repository.QuestionRepository;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import static com.vista.questionnaire.domain.ParamsContents.*;

@Service
public class ValidAndSaveAnswerService {
    private final ExaminationRepository examinationRepository;
    private final AnswerRepository answerRepository;
    private final AnswerDetailRepository answerDetailRepository;
    private final QuestionRepository questionRepository;

    public ValidAndSaveAnswerService(ExaminationRepository examinationRepository, AnswerRepository answerRepository, AnswerDetailRepository answerDetailRepository, QuestionRepository questionRepository) {
        this.examinationRepository = examinationRepository;
        this.answerRepository = answerRepository;
        this.answerDetailRepository = answerDetailRepository;
        this.questionRepository = questionRepository;
    }

    public Answer save(JSONObject answerJson) throws JSONException {
        Integer id = answerJson.getInt(EXAMINATION);
        Examination examination = this.examinationRepository.findOne(id.longValue());
        if (examination == null) throw new BizException(ErrorDescriptor.DataNotFoundError);
        List<Question> questions = examination.getQuestions();
        List<JSONObject> jsonObjects = this.convertJsonArrayToList(answerJson.getJSONArray(DATA));
        List<AnswerDetail> details = new ArrayList<>();
        jsonObjects.forEach(jsonObject -> {
            Question questionValid = questions.stream().filter(question -> {
                try {
                    return question.getName().equals(jsonObject.getString(NAME));
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                return false;
            }).findFirst().get();
            try {
                AnswerDetail answerDetail = validPass(examination, questionValid, jsonObjects);
                if (!details.stream().anyMatch(answerDetail1 -> answerDetail1.getQuestion().equals(answerDetail.getQuestion())))
                    details.add(answerDetail);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        });
        Answer answer = new Answer();
        if (answerJson.has(ID))
            answer.setId(answerJson.getLong(ID));
        answer.setExamination(examination);
        answer.setContent(answerJson.get(DATA).toString());
        answer.setAnswerDetails(details);
        return answer;
    }


    private AnswerDetail validPass(Examination examination, Question question, List<JSONObject> jsonObjects) throws JSONException {
        Document document = Jsoup.parse(question.getContent());
        List<Element> inputElements = document.getElementsByTag(INPUT);
        Attributes attributes = inputElements.get(0).attributes();
        String name = attributes.get(NAME);
        AnswerDetail answerDetail = new AnswerDetail();

        answerDetail.setQuestion(question);
        answerDetail.setExamination(examination);
        switch (question.getType()) {
            case CHECKBOX:
                return this.checkBox(answerDetail, attributes, jsonObjects, name, inputElements);
            case RADIO:
                return this.radio(answerDetail, jsonObjects, name);
            case TEXT:
                return this.text(answerDetail, attributes, jsonObjects, name);
            default:
                return null;
        }
    }

    private List<JSONObject> convertJsonArrayToList(JSONArray jsonArray) throws JSONException {
        List<JSONObject> jsonObjects = new ArrayList<>();
        for (int i = 0; i < jsonArray.length(); i++)
            jsonObjects.add(jsonArray.getJSONObject(i));
        return jsonObjects;
    }

    private AnswerDetail text(
        AnswerDetail answerDetail,
        Attributes attributes,
        List<JSONObject> jsonObjects,
        String name) throws JSONException {
        boolean isDigital = Boolean.parseBoolean(attributes.get(Digital));
        boolean isRequire = Boolean.parseBoolean(attributes.get(Require));
        if (isRequire) {
            if (isDigital) {
                Double min = Double.parseDouble(attributes.get(Min));
                Double max = Double.parseDouble(attributes.get(Max));
                Double value = jsonObjects.stream().filter(jsonObject -> {
                    try {
                        return jsonObject.get(NAME).equals(name);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    return false;
                }).findFirst().get().getDouble(VALUE);
                if (min > value || max < value)
                    throw new BizException(ErrorDescriptor.InputPraseError);
                else {
                    answerDetail.setValue(value.toString());
                    return answerDetail;
                }
            } else {
                int minLength = Integer.parseInt(attributes.get(MinLength));
                int maxLength = Integer.parseInt(attributes.get(MaxLength));
                String value = jsonObjects.stream().filter(jsonObject -> {
                    try {
                        return jsonObject.get(NAME).equals(name);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    return false;
                }).findFirst().get().getString(VALUE);
                if (minLength > value.length() || maxLength < value.length())
                    throw new BizException(ErrorDescriptor.InputPraseError);
                else {
                    answerDetail.setValue(value);
                    return answerDetail;
                }
            }
        } else
            return this.setValue(answerDetail, jsonObjects, name);

    }

    private AnswerDetail radio(
        AnswerDetail answerDetail,
        List<JSONObject> jsonObjects,
        String name
    ) throws JSONException {
        if (jsonObjects.stream().filter(jsonObject -> {
            try {
                return jsonObject.get(NAME).equals(name);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return false;
        }).count() == 1L) {
            return this.setValue(answerDetail, jsonObjects, name);
        } else
            throw new BizException(ErrorDescriptor.InputPraseError);
    }

    private AnswerDetail setValue(AnswerDetail answerDetail, List<JSONObject> jsonObjects, String name) throws JSONException {
        answerDetail.setValue(jsonObjects.stream().filter(jsonObject -> {
            try {
                return jsonObject.get(NAME).equals(name);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return false;
        }).findFirst()
            .get().getString(VALUE));
        return answerDetail;
    }


    /**
     * 多选题校验
     *
     * @param answerDetail
     * @param attributes
     * @param jsonObjects
     * @param name
     * @param inputElements
     * @return
     * @throws JSONException
     */
    private AnswerDetail checkBox(
        AnswerDetail answerDetail,
        Attributes attributes,
        List<JSONObject> jsonObjects,
        String name,
        List<Element> inputElements) throws JSONException {
        String maxLength = attributes.get(MaxLength);
        String minLength = attributes.get(MinLength);
        int max = maxLength.equals("") ? inputElements.size() : Integer.parseInt(maxLength);
        int min = minLength.equals("") ? 1 : Integer.parseInt(minLength);
        boolean isRequire = Boolean.parseBoolean(attributes.get(Require));
        if (isRequire && min < 1) min = 1;
        int count = 0;
        int answerCount = (int) jsonObjects.stream().filter(jsonObject -> {
            try {
                return jsonObject.get(NAME).equals(name);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return false;
        }).count();
        if (max < answerCount && answerCount <= min)
            throw new BizException(ErrorDescriptor.DataLogicError);
        //todo   Stream only use at once!!!Needs to improve ,because counts and stream itself bose useful
        for (JSONObject jsonObject : jsonObjects) {
            //get in question,start valid answer by rule def in Question.content
            if (jsonObject.get(NAME).equals(name)) {
                if (inputElements.stream().filter(element ->
                    {
                        try {
                            return element.attributes().get(VALUE).equals(jsonObject.getString(VALUE));
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                        return false;
                    }
                ).count() == 1L) {
                    if (count == 0) {
                        answerDetail.setValue(jsonObject.getString(VALUE));
                    } else if (count > 0)
                        answerDetail.setValue(answerDetail.getValue() + "," + jsonObject.getString(VALUE));
                    count++;
                } else {
                    throw new BizException(ErrorDescriptor.InputPraseError);
                }
            }
        }
        return answerDetail;
    }
}
